package com.codelee.soybean;


import com.codelee.soybean.annotation.Column;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.*;
import java.util.Date;

/**
 * row processor
 * Created by lic on 15/6/4.
 */
public class BasicProcessor implements RowProcessor{

    /**
     * parse the result to java bean
     * @param rs
     * @param clazz
     * @param <T>
     * @return
     * @throws SQLException
     */
    public <T> T toBean(ResultSet rs, Class<? extends T> clazz) throws SQLException{
        try{
            if(rs.next()){
                T t = createBean(rs,clazz);
                return t;
            }

        } catch (Exception e){
            e.printStackTrace();
        }

        return null;
    }


    /**
     * parse the resultset to List<Map>
     * @param rs
     * @return
     * @throws SQLException
     */
    public List<Map> toMapList(ResultSet rs) throws SQLException{
        List<Map> list = new ArrayList<Map>();
        while(rs.next()){
            list.add(createMap(rs));
        }
        return list;
    }

    /**
     * parse the resultset to Map
     * @param rs
     * @return
     * @throws SQLException
     */
    public Map toMap(ResultSet rs) throws SQLException{
        if(rs.next()){
            return createMap(rs);
        }
        return null;
    }


    /**
     * parse the resultset to java bean List
     * @param rs
     * @param clazz
     * @param <T>
     * @return
     * @throws SQLException
     */
    public <T> List<T> toBeanList(ResultSet rs, Class<? extends T> clazz) throws SQLException{
        List<T> list = new ArrayList<T>();
        try{
            while (rs.next()){
                T t = createBean(rs,clazz);
                list.add(t);
            }

        }catch (Exception e){
            e.printStackTrace();
        }

        return list;
    }


    /**
     * parse the resultset to number
     * @param rs
     * @return
     * @throws SQLException
     */
    public long count(ResultSet rs) throws SQLException {
        ResultSetMetaData rmd = rs.getMetaData();
        if(rmd.getColumnCount() != 1){
            throw new SQLException("resultset more than one column");
        }
        long count = 0;
        if(rs.next()){
            Object colmuns = rs.getObject(1);
            if(!colmuns.getClass().equals(Integer.TYPE) &&
                    !colmuns.getClass().equals(Integer.class) &&
                    !colmuns.getClass().equals(Long.TYPE) &&
                    !colmuns.getClass().equals(Long.class)){
                throw new SQLException("column tyoe is not a number!");
            }
            if(colmuns instanceof Integer || colmuns.getClass().equals(Integer.class)){
                count = (long)((Integer)colmuns);
            }else{
                count = (Long)colmuns;
            }



        }
        return count;
    }


    private void setValue(PropertyDescriptor propertyDescriptor , Object target , Object value) throws Exception{
        Class clazz = propertyDescriptor.getPropertyType();
        Method writeMethod = propertyDescriptor.getWriteMethod();
        if(clazz.equals(Boolean.TYPE) || clazz.equals(Boolean.class)){
            writeMethod.invoke(target,(Boolean)value);
        }
        if(clazz.equals(Integer.TYPE) || clazz.equals(Integer.class)){

            writeMethod.invoke(target , (Integer)value);
        }
        if(clazz.equals(Float.TYPE) || clazz.equals(Float.class)){
            writeMethod.invoke(target,(Float)value);
        }
        if(clazz.equals(Double.TYPE) || clazz.equals(Double.class)){
            writeMethod.invoke(target,(Double)value);
        }
        if(clazz.equals(Byte.TYPE) || clazz.equals(Byte.class)){
            writeMethod.invoke(target,(Byte)value);
        }
        if(clazz.equals(Long.TYPE) || clazz.equals(Long.class)){
            writeMethod.invoke(target,(Long)value);
        }
        if(clazz.equals(Short.TYPE) || clazz.equals(Short.class)){
            writeMethod.invoke(target,(Short)value);
        }
        if(clazz.getName().equals("java.lang.String")){
            writeMethod.invoke(target,(String)value);
        }
        if(clazz.equals(Date.class)){
            writeMethod.invoke(target,new Date(((Date)value).getTime()));
        }
        if(clazz.equals(Timestamp.class)){
            writeMethod.invoke(target,new Timestamp(((Date)value).getTime()));
        }
        if(clazz.equals(Time.class)){
            writeMethod.invoke(target,new Time(((Date)value).getTime()));
        }
    }



    private PropertyDescriptor getPd(Class clazz , String name) throws Exception{
        PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(clazz).getPropertyDescriptors();

        PropertyDescriptor pd = getPd(propertyDescriptors,name);

        if(pd == null){
            Field[] fields = clazz.getDeclaredFields();
            for(Field field : fields){

                Column column = field.getAnnotation(Column.class);
                if(column != null){
                    String value = column.value();
                    if(value.equals(name)){
                        return getPd(propertyDescriptors,field.getName());
                    }
                }

            }
        }else{
            return pd;
        }
        return null;
    }

    private PropertyDescriptor getPd(PropertyDescriptor[] pds, String filedName){
        for(PropertyDescriptor p : pds){
            if(p.getName().equals(filedName)){

                return p;
            }
        }
        return null;
    }


    private <T>T createBean(ResultSet rs , Class<? extends T> clazz) throws Exception{

        T t = clazz.newInstance();
        ResultSetMetaData rmd = rs.getMetaData();
        int count = rmd.getColumnCount();
        for(int i=1;i<=count;i++){
            String colName = rmd.getColumnLabel(i);
            if(colName == null){
                colName = rmd.getColumnName(i);
            }
            PropertyDescriptor pd = getPd(clazz , colName);
            Object value = rs.getObject(colName);

            if(pd != null && value != null){

                setValue(pd, t , value);
            }
        }
        return t;
    }


    private Map<String,Object> createMap(ResultSet rs) throws SQLException{
        Map<String,Object> map = new HashMap<String, Object>();
        ResultSetMetaData rmd = rs.getMetaData();
        int colCount = rmd.getColumnCount();
        for(int i=1;i<=colCount;i++){
            String colName = rmd.getColumnLabel(i);
            if(colName == null){
                colName = rmd.getColumnName(i);
            }
            map.put(colName, rs.getObject(colName));
        }
        return map;
    }
}
